package com.yqb.study.aop.exception;

import java.util.List;

import javax.servlet.http.HttpServletRequest;

import org.springframework.core.Ordered;
import org.springframework.http.HttpStatus;
import org.springframework.validation.BindException;
import org.springframework.validation.ObjectError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;

import com.yqb.study.aop.AopOrderEnum;
import com.yqb.study.common.bean.BeanFactory;
import com.yqb.study.common.exception.GlobalException;
import com.yqb.study.common.log.LogObjectHolder;
import com.yqb.study.common.log.LogPersistManager;
import com.yqb.study.common.log.LogPersistTaskFactory;
import com.yqb.study.common.vo.ResultVo;
import com.yqb.study.constant.CodeMsgEnum;
import com.yqb.study.constant.log.LogTypeEnum;
import com.yqb.study.modular.persistence.model.OperateLog;
import com.yqb.study.support.HttpKit;
import com.yqb.study.util.ResultVoUtil;
import com.yqb.study.util.ToolUtil;

import lombok.extern.slf4j.Slf4j;

/**
 * 全局异常处理
 */
@ControllerAdvice
@ResponseBody
@Slf4j
public class GlobalExceptionHandler implements Ordered {
	@ExceptionHandler(value = Exception.class)
	@ResponseStatus(HttpStatus.ACCEPTED) // 返回200,所有的服务器返回情况都在success中处理
	public ResultVo exceptionHandler(HttpServletRequest request, Exception e) {		
		log.error(e.getMessage(), e);		

		ResultVo resultVo = null;	
		if (e instanceof GlobalException) {
			// 主动抛出的GlobalException异常
			GlobalException ex = (GlobalException) e;
			
			resultVo = ResultVoUtil.error(ex);			
		} else if (e instanceof BindException) {
			// JSR303不与@RequestBody一起使用抛出的BindException参数校验异常
			BindException ex = (BindException) e;
			List<ObjectError> errors = ex.getAllErrors();
			ObjectError error = errors.get(0);
			String msg = error.getDefaultMessage();
			
			resultVo = ResultVoUtil.error(new GlobalException(CodeMsgEnum.LOGIN_PARA_ERROR, msg));
		} else if(e instanceof MethodArgumentNotValidException) {
			// JSR303与@RequestBody一起使用参数校验会抛出该异常
			MethodArgumentNotValidException ex = (MethodArgumentNotValidException) e;
			List<ObjectError> errors = ex.getBindingResult().getAllErrors();
			ObjectError error = errors.get(0);
			String msg = error.getDefaultMessage();			
			
			resultVo = ResultVoUtil.error(new GlobalException(CodeMsgEnum.LOGIN_PARA_ERROR, msg));
		} else {
			// Exception是最终未知异常
			resultVo = ResultVoUtil.error(CodeMsgEnum.SERVER_ERROR);
		}
		
		// 异步保存日志
		logPersist(resultVo, e);			
		
		return resultVo;		
	}
	
	private void logPersist(ResultVo resultVo, Exception e) {		
		OperateLog operateLog = LogObjectHolder.me().getOperateLog();
		if (ToolUtil.isNotEmpty(operateLog)) {
			LogPersistManager.me().execute(LogPersistTaskFactory.operateLog(BeanFactory.createOperateLog(LogTypeEnum.EXCEPT_LOG.getNum().toString(), operateLog.getLogName(), 11111, operateLog.getClassName(), operateLog.getMethod(), "失败", HttpKit.getIp(), operateLog.getRequest(), e.toString())));	
		} else {
			// **************** JSR303参数校验在日志AOP之前,很麻烦????  **********************
			LogPersistManager.me().execute(LogPersistTaskFactory.operateLog(BeanFactory.createOperateLog(LogTypeEnum.EXCEPT_LOG.getNum().toString(), resultVo.getMsg(), 11111, null, null, "失败", HttpKit.getIp(), HttpKit.getRequestParameters().toString(), e.toString())));
		}	
		
		// 清空
		LogObjectHolder.me().setOperateLog(null);		
	}
	
	@Override
	public int getOrder() {
		return AopOrderEnum.EXCEPT_AOP.getNum();
	}		
}
